From 18a396beb9bb121430e15ec8a586b33a7e152144 Mon Sep 17 00:00:00 2001 From: Debian LibreOffice Maintainers Date: Sat, 3 Oct 2026 12:46:08 +0200 Subject: [PATCH] firebird-db-connection MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit commit 16cf06caff90c0e4d9fe2fc24eac378ed533c48d Author: Caolán McNamara Date: Tue Jul 28 11:05:37 2026 +0000 Resolves: tdf#172935 Connection from Base to external firebird database refused there is one firebird engine per process and it reads its configuration once from the directory we set up, so a .fdb the user picked somewhere else could not be attached. firebird resolves a name in databases.conf before it treats it as a path, so give the file a name there and pass firebird that name, dropped again when the connection is disposed. since: commit 520d8173cef39a4da85b3ba21bdaaa3150384c25 Date: Sun Jul 12 12:10:41 2026 +0000 firebird: keep each embedded database's files in one directory Change-Id: I4872a4e2165c4701bfb20be5dcd8c119b1723125 Signed-off-by: Caolán McNamara Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/7990 Tested-by: Jenkins CPCI Reviewed-by: Michael Stahl (cherry picked from commit 6ab71a109bd8a8335208c9faa37a3cfa94523148) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208773 Tested-by: Jenkins Reviewed-by: Xisco Fauli (cherry picked from commit 99bf5222896886d062b6a6a67377e7671de1446a) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208855 Gbp-Pq: Name firebird-db-connection.diff --- .../source/drivers/firebird/Connection.cxx | 28 +++- .../source/drivers/firebird/Connection.hxx | 18 +- .../source/drivers/firebird/Driver.cxx | 155 ++++++++++++++++-- .../source/drivers/firebird/Driver.hxx | 21 +++ 4 files changed, 201 insertions(+), 21 deletions(-) diff --git a/connectivity/source/drivers/firebird/Connection.cxx b/connectivity/source/drivers/firebird/Connection.cxx index c5e1e958af6..edac670bdbf 100644 --- a/connectivity/source/drivers/firebird/Connection.cxx +++ b/connectivity/source/drivers/firebird/Connection.cxx @@ -22,6 +22,7 @@ #include "Clob.hxx" #include "Connection.hxx" #include "DatabaseMetaData.hxx" +#include "Driver.hxx" #include "PreparedStatement.hxx" #include "Statement.hxx" #include "Util.hxx" @@ -156,13 +157,14 @@ bool databaseHeaderHasBackupState(const OUString& rDatabasePath) } void Connection::construct(const OUString& url, const Sequence< PropertyValue >& info, - const OUString& rDatabaseDataDirectoryURL) + FirebirdDriver& rDriver) { ConnectionGuard aGuard(m_refCount); try { m_sConnectionURL = url; + m_xDriver = &rDriver; bool bIsNewDatabase = false; // the database may be stored as an @@ -196,7 +198,8 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >& bIsNewDatabase = !m_xEmbeddedStorage->hasElements(); - m_pDatabaseFileDir.reset(new ::utl::TempFileNamed(&rDatabaseDataDirectoryURL, true)); + const OUString sDatabaseDataDirectoryURL = rDriver.getDatabaseDataDirectoryURL(); + m_pDatabaseFileDir.reset(new ::utl::TempFileNamed(&sDatabaseDataDirectoryURL, true)); m_pDatabaseFileDir->EnableKillingFile(); m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb"; m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk"; @@ -250,6 +253,10 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >& bIsNewDatabase = true; osl::FileBase::getSystemPathFromFileURL(m_sFirebirdURL, m_sFirebirdURL); + + // The user picked this file, so it is outside the directory firebird is + // restricted to. Reach it by the name the driver gives us for it. + m_sExternalDatabaseName = rDriver.addExternalDatabaseName(m_sFirebirdURL); } } @@ -327,12 +334,13 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >& ISC_STATUS_ARRAY status; /* status vector */ ISC_STATUS aErr; - const OString sFirebirdURL = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8); + const OString sDatabaseName + = OUStringToOString(getFirebirdDatabaseName(), RTL_TEXTENCODING_UTF8); if (bIsNewDatabase) { aErr = isc_create_database(status, - sFirebirdURL.getLength(), - sFirebirdURL.getStr(), + sDatabaseName.getLength(), + sDatabaseName.getStr(), &m_aDBHandle, dpbBuffer.size(), dpbBuffer.c_str(), @@ -350,8 +358,8 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >& } aErr = isc_attach_database(status, - sFirebirdURL.getLength(), - sFirebirdURL.getStr(), + sDatabaseName.getLength(), + sDatabaseName.getStr(), &m_aDBHandle, dpbBuffer.size(), dpbBuffer.c_str()); @@ -951,6 +959,12 @@ void Connection::disposing() storeDatabase(); + if (!m_sExternalDatabaseName.isEmpty()) + { + m_xDriver->removeExternalDatabaseName(m_sExternalDatabaseName); + m_sExternalDatabaseName.clear(); + } + cppu::WeakComponentImplHelperBase::disposing(); m_pDatabaseFileDir.reset(); diff --git a/connectivity/source/drivers/firebird/Connection.hxx b/connectivity/source/drivers/firebird/Connection.hxx index c680c20db73..0a56de4fb9e 100644 --- a/connectivity/source/drivers/firebird/Connection.hxx +++ b/connectivity/source/drivers/firebird/Connection.hxx @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,21 @@ namespace connectivity::firebird /** We are using an external (local) file */ bool m_bIsFile; + /** The driver that made this connection, and owns the directory firebird works in. */ + rtl::Reference m_xDriver; + + /** + * The name firebird was given for a database outside its own directory, empty when the + * database is inside it and its path was handed over directly. + */ + OUString m_sExternalDatabaseName; + + /** The name or path this connection hands to firebird to reach its database. */ + const OUString& getFirebirdDatabaseName() const + { + return m_sExternalDatabaseName.isEmpty() ? m_sFirebirdURL : m_sExternalDatabaseName; + } + /** When true the embedded database was opened in this session, so the temporary .fdb holds live data that must be written back into the .odb when the connection is disposed. */ @@ -169,7 +185,7 @@ namespace connectivity::firebird /// @throws css::uno::RuntimeException void construct( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info, - const OUString& rDatabaseDataDirectoryURL); + FirebirdDriver& rDriver); const OUString& getConnectionURL() const {return m_sConnectionURL;} bool isEmbedded() const {return m_bIsEmbedded;} diff --git a/connectivity/source/drivers/firebird/Driver.cxx b/connectivity/source/drivers/firebird/Driver.cxx index 1f345d772ac..d02de40377b 100644 --- a/connectivity/source/drivers/firebird/Driver.cxx +++ b/connectivity/source/drivers/firebird/Driver.cxx @@ -25,13 +25,37 @@ #include #include +#include + +// firebird's own header has functions with parameters it does not use +#if defined __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4100) // unreferenced formal parameter +#endif +#include +#if defined _MSC_VER +#pragma warning(pop) +#endif +#if defined __GNUC__ +#pragma GCC diagnostic pop +#endif + #include #include #include #include +#include #include +#include +#include #include +#include + using namespace com::sun::star; using namespace com::sun::star::uno; using namespace com::sun::star::lang; @@ -52,6 +76,66 @@ constexpr OUString our_sFirebirdRootVar = u"FIREBIRD"_ustr; #ifdef MACOSX constexpr OUString our_sFirebirdLibVar = u"LIBREOFFICE_FIREBIRD_LIB"_ustr; #endif + +// The name of the file in the firebird directory that maps a name to the absolute path of a +// database. firebird looks a name up here before it treats it as a path, and re-reads the file +// whenever it has changed. +constexpr OUString our_sDatabasesConfName = u"databases.conf"_ustr; + +// Put rContent in the file at rURL, discarding whatever was there before. +bool writeFile(const OUString& rURL, std::string_view aContent) +{ + ::osl::File aFile(rURL); + if (aFile.open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write) != ::osl::FileBase::E_None) + { + if (aFile.open(osl_File_OpenFlag_Write) != ::osl::FileBase::E_None) + return false; + if (aFile.setSize(0) != ::osl::FileBase::E_None) + { + aFile.close(); + return false; + } + } + + bool bWholeContentWritten = true; + if (!aContent.empty()) + { + sal_uInt64 nWritten = 0; + bWholeContentWritten + = aFile.write(aContent.data(), aContent.size(), nWritten) == ::osl::FileBase::E_None + && nWritten == aContent.size(); + } + aFile.close(); + return bWholeContentWritten; +} + +// firebird takes its root directory from the FIREBIRD variable on its first use, and reads the +// firebird.conf that confines it to that directory only from there. Ask firebird which directory +// it settled on and treat anything but our own as unconfined. +bool isFirebirdRootedAt(const OUString& rDataDirPath) +{ + Firebird::IMaster* pMaster = Firebird::fb_get_master_interface(); + if (!pMaster) + return false; + + Firebird::IConfigManager* pConfigManager = pMaster->getConfigManager(); + if (!pConfigManager) + return false; + + const char* pRootDirectory = pConfigManager->getRootDirectory(); + if (!pRootDirectory) + return false; + + OUString sRootPath = OStringToOUString(pRootDirectory, osl_getThreadTextEncoding()); + if (sRootPath.endsWith("/") || sRootPath.endsWith("\\")) + sRootPath = sRootPath.copy(0, sRootPath.getLength() - 1); + +#if defined(_WIN32) + return sRootPath.equalsIgnoreAsciiCase(rDataDirPath); +#else + return sRootPath == rDataDirPath; +#endif +} }; FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext) @@ -90,25 +174,25 @@ FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentCo sDataDirPath = sDataDirPath.toAsciiUpperCase(); #endif + // ExternalFileAccess is already None by default. Saying so here keeps a database firebird + // opens from reading databases.conf back out of the directory it shares with it. OString sConf = "DatabaseAccess = Restrict " - + OUStringToOString(sDataDirPath, RTL_TEXTENCODING_UTF8) + "\n"; + + OUStringToOString(sDataDirPath, RTL_TEXTENCODING_UTF8) + + "\nExternalFileAccess = None\n"; OUString sConfURL = m_firebirdDataDirectory.GetURL() + "/firebird.conf"; - ::osl::File aConfFile(sConfURL); - if (aConfFile.open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write) == ::osl::FileBase::E_None) + if (writeFile(sConfURL, std::string_view(sConf)) + // An empty databases.conf, so that firebird has the file in hand from its first use and + // notices the names added to it later. + && writeFile(m_firebirdDataDirectory.GetURL() + "/" + our_sDatabasesConfName, + std::string_view())) { - sal_uInt64 nWritten = 0; - if (aConfFile.write(sConf.getStr(), sConf.getLength(), nWritten) == ::osl::FileBase::E_None - && nWritten == static_cast(sConf.getLength())) - { - osl_setEnvironment(our_sFirebirdRootVar.pData, sDataDirPath.pData); - m_bConfined = true; - } - aConfFile.close(); + osl_setEnvironment(our_sFirebirdRootVar.pData, sDataDirPath.pData); + m_bConfined = isFirebirdRootedAt(sDataDirPath); } SAL_WARN_IF(!m_bConfined, "connectivity.firebird", - "could not write " << sConfURL << ", firebird connections will be refused"); + "firebird is not confined to " << sDataDirPath << ", connections will be refused"); #ifndef SYSTEM_FIREBIRD // Overrides firebird's hardcoded default of /usr/local/firebird on *nix, @@ -132,6 +216,51 @@ FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentCo FirebirdDriver::~FirebirdDriver() = default; +OUString FirebirdDriver::addExternalDatabaseName(const OUString& rDatabasePath) +{ + MutexGuard aGuard(m_aMutex); + + sal_uInt8 aUuid[16]; + rtl_createUuid(aUuid, nullptr, false); + + OUStringBuffer aName(u"database"); + for (const sal_uInt8 nByte : aUuid) + { + aName.append(OUString::number(nByte >> 4, 16) + OUString::number(nByte & 0x0F, 16)); + } + const OUString sName = aName.makeStringAndClear(); + + m_aExternalDatabaseNames.emplace(sName, rDatabasePath); + writeExternalDatabaseNames(); + + return sName; +} + +void FirebirdDriver::removeExternalDatabaseName(const OUString& rName) +{ + MutexGuard aGuard(m_aMutex); + + if (m_aExternalDatabaseNames.erase(rName) != 0) + writeExternalDatabaseNames(); +} + +void FirebirdDriver::writeExternalDatabaseNames() +{ + MutexGuard aGuard(m_aMutex); + + OStringBuffer aContent; + for (const auto& [rName, rPath] : m_aExternalDatabaseNames) + { + aContent.append(OUStringToOString(rName, RTL_TEXTENCODING_UTF8) + " = " + + OUStringToOString(rPath, RTL_TEXTENCODING_UTF8) + "\n"); + } + const OString sContent = aContent.makeStringAndClear(); + + const OUString sURL = m_firebirdDataDirectory.GetURL() + "/" + our_sDatabasesConfName; + SAL_WARN_IF(!writeFile(sURL, std::string_view(sContent)), "connectivity.firebird", + "could not write " << sURL); +} + void FirebirdDriver::disposing() { MutexGuard aGuard(m_aMutex); @@ -199,7 +328,7 @@ Reference< XConnection > SAL_CALL FirebirdDriver::connect( } rtl::Reference pCon = new Connection(); - pCon->construct(url, info, getDatabaseDataDirectoryURL()); + pCon->construct(url, info, *this); m_xConnections.emplace_back(*pCon); diff --git a/connectivity/source/drivers/firebird/Driver.hxx b/connectivity/source/drivers/firebird/Driver.hxx index 0f5172f6222..060ca5d90fe 100644 --- a/connectivity/source/drivers/firebird/Driver.hxx +++ b/connectivity/source/drivers/firebird/Driver.hxx @@ -28,6 +28,8 @@ #include #include +#include + namespace connectivity::firebird { // The SQL dialect in use @@ -50,6 +52,14 @@ namespace connectivity::firebird ::utl::TempFileNamed m_firebirdDataDirectory; bool m_bConfined; + // The names firebird may use for a database that lies outside its own directory, each + // mapped to that database's absolute path. The names are random, and only the databases + // the user picked in this session are in here. + std::map m_aExternalDatabaseNames; + + // Write m_aExternalDatabaseNames out as the databases.conf of the firebird directory. + void writeExternalDatabaseNames(); + protected: ::osl::Mutex m_aMutex; // mutex is need to control member access OWeakRefArray m_xConnections; // vector containing a list @@ -67,6 +77,17 @@ namespace connectivity::firebird // associated files it creates for them under this directory as well. OUString getDatabaseDataDirectoryURL() const { return m_firebirdDataDirectory.GetURL(); } + // Give firebird a name for a database that lies outside its own directory, and return + // that name. firebird resolves a name it finds in databases.conf without applying the + // DatabaseAccess restriction, while every path it is handed elsewhere, a difference file + // or a shadow, is still checked against that restriction. The name is random so that a + // database firebird opens cannot ask for another database by guessing the name. + OUString addExternalDatabaseName(const OUString& rDatabasePath); + + // Take back a name from addExternalDatabaseName. Any database already attached through + // it stays open. + void removeExternalDatabaseName(const OUString& rName); + // OComponentHelper virtual void SAL_CALL disposing() override; -- 2.39.5